package com.salt.core.excel.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.DateUtils;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.salt.core.excel.exception.ExcelException;
import com.salt.core.excel.listener.DataListener;
import com.salt.core.excel.listener.ImportListener;
import com.salt.core.excel.service.ExcelImporter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.Charsets;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;

/**
 * @Description Excel工具类
 * @Author salt
 * @Date 2021-05-25
 * @Version 1.0.1
 */
@Slf4j
public class ExcelUtil {


    /**
     * 读取并导入数据
     * @param excel excel文件
     * @param importer excel监听类
     * @param clazz Class类
     * @param <T> 范型
     */
    public static <T> void save(MultipartFile excel, ExcelImporter<T> importer, Class<T> clazz) {
        ImportListener<T> importListener = new ImportListener<>(importer);
        ExcelReaderBuilder builder = getReaderBuilder(excel, importListener, clazz);
        if (builder != null) {
            builder.doReadAll();
        }
    }

    /**
     * 读取并导入数据
     * @param excel excel文件
     * @param importer excel监听类
     * @param sheetNo sheet序号(从0开始)
     * @param headRowNum 表头行数
     * @param clazz Class类
     * @param <T> 范型
     */
    public static <T> void save(MultipartFile excel, ExcelImporter<T> importer, int sheetNo, int headRowNum, Class<T> clazz) {
        ImportListener<T> importListener = new ImportListener<>(importer);
        ExcelReaderBuilder builder = getReaderBuilder(excel, importListener, clazz);
        if (builder != null) {
            builder.sheet(sheetNo).headRowNumber(headRowNum).doRead();
        }
    }

    /**
     * 读取excel的所有sheet的数据
     * @param excel excel文件
     * @param clazz Class类
     * @param <T> 范型
     * @return
     */
    public static <T> List<T> read(MultipartFile excel, Class<T> clazz) {
        DataListener<T> dataListener = new DataListener<>();
        ExcelReaderBuilder builder = getReaderBuilder(excel, dataListener, clazz);
        if (builder != null) {
            builder.doReadAll();
            return dataListener.getDataList();
        }
        return null;
    }

    /**
     * 读取excel中指定的sheet里面的数据(表头行数默认1)
     * @param excel excel文件
     * @param sheetNo sheet序号(从0开始)
     * @param clazz Class类
     * @param <T> 范型
     * @return
     */
    public static <T> List<T> read(MultipartFile excel, int sheetNo, Class<T> clazz) {
        return read(excel, sheetNo, 1, clazz);
    }

    /**
     * 读取excel中指定的sheet里面的数据
     * @param excel excel文件
     * @param sheetNo sheet序号(从0开始)
     * @param headRowNum 表头行数
     * @param clazz Class类
     * @param <T> 范型
     * @return
     */
    public static <T> List<T> read(MultipartFile excel, int sheetNo, int headRowNum, Class<T> clazz) {
        DataListener<T> dataListener = new DataListener<>();
        ExcelReaderBuilder builder = getReaderBuilder(excel, dataListener, clazz);
        if (builder != null) {
            builder.sheet(sheetNo).headRowNumber(headRowNum).doRead();
            return dataListener.getDataList();
        }
        return null;
    }

    /**
     * 导出Excel
     * @param response 响应
     * @param dataList 数据列表
     * @param clazz Class类
     * @param <T> 范型
     */
    public static <T> void export(HttpServletResponse response, List<T> dataList, Class<T> clazz) {
        export(response, DateUtils.format(new Date(), DateUtils.DATE_FORMAT_14), "导出数据", dataList, clazz);
    }

    /**
     * 导出Excel
     * @param response 响应
     * @param fileName 文件名
     * @param sheetName sheet名
     * @param dataList 数据列表
     * @param clazz Class类
     * @param <T> 范型
     */
    @SneakyThrows
    public static <T> void export(HttpServletResponse response, String fileName, String sheetName, List<T> dataList, Class<T> clazz) {
        // 设置表头
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);// 设置表头居中
        // 设置内容
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);// 设置表内容居中
        HorizontalCellStyleStrategy hcss = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);

        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding(Charsets.UTF_8.name());
        fileName = URLEncoder.encode(fileName, Charsets.UTF_8.name());
        response.setHeader("Content-disposition",  fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), clazz)
                .registerWriteHandler(hcss)
                .sheet()
                .sheetName(sheetName)
                .doWrite(dataList);
    }


    /**
     * 获取构建类
     * @param excel excel文件
     * @param readListener excel监听类
     * @param clazz Class类
     * @param <T> 范型
     * @return
     */
    public static <T> ExcelReaderBuilder getReaderBuilder(MultipartFile excel, ReadListener<T> readListener, Class<T> clazz) {
        String filename = excel.getOriginalFilename();
        if (filename.isEmpty()) {
            throw new ExcelException("请上传文件");
        }
        if (!StringUtils.endsWithIgnoreCase(filename, ".xls") &&
                !StringUtils.endsWithIgnoreCase(filename, ".xlsx")) {
            throw new ExcelException("请上传正确的excel文件!");
        }
        InputStream inputStream;
        try {
            inputStream = new BufferedInputStream(excel.getInputStream());
            return EasyExcel.read(inputStream, clazz, readListener);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
